package cn.sccl.dirms.topo.discovery.service;

import cn.sccl.dirms.topo.discovery.model.DeviceInfo;
import cn.sccl.dirms.topo.discovery.model.DiscoveryConfig;
import cn.sccl.dirms.topo.discovery.model.LinkInfo;
import cn.sccl.dirms.topo.discovery.model.PortInfo;
import cn.sccl.dirms.topo.discovery.util.Constants;
import cn.sccl.dirms.topo.discovery.util.IfLine;
import cn.sccl.dirms.topo.discovery.util.IfPoint;
import cn.sccl.dirms.topo.discovery.util.REUtil;
import cn.sccl.dirms.topo.snmp.mib.*;

import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;


public class DiscoveryRelationService {

    DeviceInfoManager deviceManager;
    List<LinkInfo> allLinkList = new ArrayList<LinkInfo>();

    public List<LinkInfo> createAllLinks(List<DeviceInfo> deviceInfoList, DiscoveryConfig discoveryConfig) {
        this.deviceManager = new DeviceInfoManager(deviceInfoList);
        DiscoveryLLDPLinks(deviceInfoList);
        DiscoveryCDPLinks(deviceInfoList);
        DiscoveryOspfNbrLinks(deviceInfoList);
        DiscoveryIPRouterLinks(deviceInfoList);//DiscoveryIPRouterLinks
//        DiscoveryArpLinks(deviceInfoList);//DiscoveryIPRouterLinks
        DiscoveryStpLinks(deviceInfoList);
//        DiscoveryMacLinks(deviceInfoList);
        DiscoveryPortDataLinks(deviceInfoList);
        return this.allLinkList;
    }


    /**
     * 根据能够确定的三层交换机端口--连接的二层交换机，以及二层交换机之间的端口流量趋势确定连接关系
     *
     * @param deviceInfoList
     */
    private void DiscoveryPortDataLinks(List<DeviceInfo> deviceInfoList) {
        //第一步：在所有设备的端口中筛选出连接了设备的真实端口（一般是三层交换机连接到二层）
        //筛选方法：dot1dTpFdbTable .1.3.6.1.2.1.17.4.3.1.2.+mac转10进制 = mac所在的端口号。M
        //如果一个设备下同一个端口存在多个设备接入，说明这些设备存在互联关系，将其筛选出来
        Map<String, Map<Integer, List<DeviceInfo>>> ipPortDeviceMap = new HashMap<String, Map<Integer, List<DeviceInfo>>>();
        List<String> ips = new ArrayList<String>();
        for (DeviceInfo deviceInfo : deviceInfoList) {
            Map<Integer, List<DeviceInfo>> deviceRelation = ipPortDeviceMap.get(deviceInfo.getDeviceIP());
            if (deviceRelation == null) {
                deviceRelation = new HashMap<Integer, List<DeviceInfo>>();
            }
            List<Dot1dTpFdbEntry> fdbList = deviceInfo.getDot1dTpFdbEntries();
            for (Dot1dTpFdbEntry dot1dTpFdbEntry : fdbList) {
                DeviceInfo relationDevice = this.deviceManager.getDeviceInfoByMAC(dot1dTpFdbEntry.getDot1dTpFdbAddress());
                if (relationDevice != null) {
                    List<DeviceInfo> relationList = deviceRelation.get(dot1dTpFdbEntry.getDot1dTpFdbPort());
                    if (relationList == null) {
                        relationList = new ArrayList<DeviceInfo>();
                    }
                    relationList.add(relationDevice);
                    deviceRelation.put(dot1dTpFdbEntry.getDot1dTpFdbPort(), relationList);
                }
            }
            ipPortDeviceMap.put(deviceInfo.getDeviceIP(), deviceRelation);
            ips.add(deviceInfo.getDeviceIP());
        }
        //读取流量快照
        Map<String, Map<Integer, List<IfPoint>[]>> alliPPortPackageMap = this.shortCutDevPortPackage(ips);

        //遍历筛选结果
        for (Map.Entry<String, Map<Integer, List<DeviceInfo>>> ipPortDeviceEntry : ipPortDeviceMap.entrySet()) {
            String ip = ipPortDeviceEntry.getKey();
            DeviceInfo parentDevice = deviceManager.getDeviceInfoByIp(ip);

            Map<Integer, List<DeviceInfo>> portMap = ipPortDeviceEntry.getValue();
            for (Map.Entry<Integer, List<DeviceInfo>> portEntry : portMap.entrySet()) {
                int portNum = portEntry.getKey();
                List<DeviceInfo> portDeviceList = portEntry.getValue();
                //端口下存在多个设备---分析端口流量 确定端口下设备连接关系
                if (portDeviceList.size() > 0) {
                    Map<String, Map<Integer, List<IfPoint>[]>> thisPortPackageMap = new HashMap<String, Map<Integer, List<IfPoint>[]>>();
                    thisPortPackageMap.put(parentDevice.getDeviceIP(), alliPPortPackageMap.get(parentDevice.getDeviceIP()));
                    for (DeviceInfo deviceInfo : portDeviceList) {
                        thisPortPackageMap.put(deviceInfo.getDeviceIP(), alliPPortPackageMap.get(deviceInfo.getDeviceIP()));
                    }
                    match(ip, portNum, thisPortPackageMap);
                }
            }
        }
    }

    public void match(String matchIp, int matchPort, Map<String, Map<Integer, List<IfPoint>[]>> ipInOutLineMap) {
        Object[] result = getCloseIPPort(matchIp, matchPort, ipInOutLineMap);
        String nextIp = (String) result[0];
        int nextPort = (Integer) result[1];
        addIpLink(matchIp, matchPort, nextIp, nextPort);

        Map<Integer, List<IfPoint>[]> nextPortInOutLineMap = ipInOutLineMap.get(nextIp);
        nextPortInOutLineMap.remove(nextPort);
        for (Map.Entry<String, Map<Integer, List<IfPoint>[]>> otherChildMapEntry : ipInOutLineMap.entrySet()) {
            String otherIp = otherChildMapEntry.getKey();
            Map<Integer, List<IfPoint>[]> otherMap = otherChildMapEntry.getValue();
            if (!otherIp.equals(nextIp)) {
                //当前端口对应的port和IP,得分最少的端口组合为汇聚端口
                double minPortScore = -1;
                int besPort2Port = -1;
                int bestOtherPort = -1;
                for (Map.Entry<Integer, List<IfPoint>[]> otherPortMap : otherMap.entrySet()) {
                    int port = otherPortMap.getKey();
                    Map<String, Map<Integer, List<IfPoint>[]>> newotherMap = new HashMap<String, Map<Integer, List<IfPoint>[]>>();
                    newotherMap.put(otherIp, otherMap);
                    newotherMap.put(nextIp, nextPortInOutLineMap);
                    result = getCloseIPPort(otherIp, port, newotherMap);
                    int nowport = (Integer) result[1];
                    double score = (Double) result[2];
                    if (score <= 0) {
                        continue;
                    }
                    System.out.println(otherIp + ":" + port + "--" + nextIp + ":" + nowport + ":" + score);

                    if (minPortScore == -1) {
                        besPort2Port = nowport;
                        bestOtherPort = port;
                        minPortScore = score;
                    }
                    if (score < minPortScore) {
                        besPort2Port = nowport;
                        bestOtherPort = port;
                        minPortScore = score;
                    }

                }
                nextPortInOutLineMap.remove(besPort2Port);
                addIpLink(otherIp, bestOtherPort, nextIp, besPort2Port);
            }
        }
    }

    public Object[] getCloseIPPort(String matchIp, int matchPort, Map<String, Map<Integer, List<IfPoint>[]>> ipInOutLineMap) {
        List<IfPoint>[] matchInOutLine = ipInOutLineMap.get(matchIp).get(matchPort);
        String bestMatchIp = "";
        int bestMatchPort = -1;
        double minScore = -1;
        ipInOutLineMap.remove(matchIp);
        for (Map.Entry<String, Map<Integer, List<IfPoint>[]>> entry : ipInOutLineMap.entrySet()) {
            String ip = entry.getKey();
            Map<Integer, List<IfPoint>[]> portInOutLineMap = entry.getValue();
            for (Map.Entry<Integer, List<IfPoint>[]> portEntry : portInOutLineMap.entrySet()) {
                int port = portEntry.getKey();
                List<IfPoint>[] inoutLine = portEntry.getValue();
                double score = matchInOutLine(matchInOutLine, inoutLine);
                if (score <= 0) {
                    continue;
                }
                if (minScore == -1) {
                    bestMatchIp = ip;
                    bestMatchPort = port;
                    minScore = score;
                }
                if (score < minScore) {
                    bestMatchIp = ip;
                    bestMatchPort = port;
                    minScore = score;
                }
            }
        }
        return new Object[]{bestMatchIp, bestMatchPort, minScore};
    }

    public void addIpLink(String ipA, int portA, String ipB, int portB) {
        PortInfo portInfoA = new PortInfo();
        PortInfo portInfoB = new PortInfo();
        DeviceInfo A = deviceManager.getDeviceInfoByIp(ipA);
        DeviceInfo B = deviceManager.getDeviceInfoByIp(ipB);
        portInfoA.device = A;
        portInfoA.port = portA;
        portInfoB.device = B;
        portInfoB.port = portB;
        LinkInfo linkInfo = LinkInfo.creatLink(portInfoA, portInfoB, false);
        addLink(linkInfo);
    }

    public double matchInOutLine(List<IfPoint>[] matchA, List<IfPoint>[] matchB) {
        if (matchA == null || matchB == null) {
            return 0;
        }
        List<IfPoint> inA = matchA[0];
        List<IfPoint> outA = matchA[1];
        List<IfPoint> inB = matchB[0];
        List<IfPoint> outB = matchB[1];
        double scoreIn = matchLine(inA, outB);
        double scoreOut = matchLine(outA, inB);
        return scoreIn + scoreOut;
    }


    public double matchLine(List<IfPoint> matchA, List<IfPoint> matchB) {
        if (matchA == null || matchB == null) {
            return 0;
        }
        double score = 0;
        int sizeA = matchA.size();
        int sizeB = matchB.size();
        //找出所有的拐点，形成线段
        int AbeginIndex = 0;
        int BbeginIndex = 0;
        A:
        for (int a = 0; a < sizeA; a++) {
            IfPoint ifPointA = matchA.get(a);
            for (int b = 0; b < sizeB; b++) {
                IfPoint ifPointB = matchB.get(b);
                if (ifPointA.x.equals(ifPointB.x)) {
                    AbeginIndex = a;
                    BbeginIndex = b;
                    break A;
                }
            }
        }
        //相同开始的两个曲线图
        matchA = matchA.subList(AbeginIndex, sizeA - 1);
        matchB = matchB.subList(BbeginIndex, sizeB - 1);
        //以一分钟为间隔,截取出N段比较样本
        List<List<IfPoint>> one_miniute_line_A = new ArrayList<List<IfPoint>>();
        List<List<IfPoint>> one_miniute_line_B = new ArrayList<List<IfPoint>>();
        sizeA = matchA.size();
        sizeB = matchB.size();

        int shortLineLength = Constants.interval / 1000;
        for (int i = 0; i < (sizeA / shortLineLength) - 1; i++) {
            one_miniute_line_A.add(matchA.subList((i * shortLineLength), ((i + 1) * shortLineLength) - 1));
        }
        for (int i = 0; i < (sizeB / shortLineLength) - 1; i++) {
            one_miniute_line_B.add(matchB.subList((i * shortLineLength), ((i + 1) * shortLineLength) - 1));
        }
        sizeA = one_miniute_line_A.size();
        sizeB = one_miniute_line_B.size();
        int matchTimes = 0;
        for (int i = 0; i < sizeA; i++) {
            if (i < sizeB) {
                List<IfPoint> lineAPoints = one_miniute_line_A.get(i);
                List<IfPoint> lineBPoints = one_miniute_line_B.get(i);
                //生成线段模型
                IfLine lineA = new IfLine(lineAPoints);
                IfLine lineB = new IfLine(lineBPoints);
                score = score + Math.abs(lineA.getDistance() - lineB.getDistance());
                matchTimes++;
            }
        }
        if (matchTimes > 0) {
            return score / matchTimes;
        } else {
            return score;
        }
    }

    /**
     * 读取所有设备一段时间内流量快照
     *
     * @param deviceInfos
     * @return
     */
    public Map<String, Map<Integer, List<IfPoint>[]>> shortCutDevPortPackage(List<String> deviceInfos) {
        Map<String, List<List<IfEntry>>> result = portPackagePool(deviceInfos);
        Map<String, Map<Integer, List<IfPoint>[]>> ipPortInOutPkgsMap = new HashMap<String, Map<Integer, List<IfPoint>[]>>();
        for (Map.Entry<String, List<List<IfEntry>>> ipTimeIfEntry : result.entrySet()) {
            String ip = ipTimeIfEntry.getKey();
            Map<Integer, List<IfPoint>[]> portInOutPkgs = getAvgPackIncrease(ipTimeIfEntry.getValue());
            ipPortInOutPkgsMap.put(ip, portInOutPkgs);
        }
        return ipPortInOutPkgsMap;
    }

    /**
     * 求取各端口 采集时间段内包传输总数
     *
     * @return
     */
    public Map<Integer, List<IfPoint>[]> getAvgPackIncrease(List<List<IfEntry>> timeIfListMapList) {
        Map<Integer, List<IfPoint>[]> result = new HashMap<Integer, List<IfPoint>[]>();
        //采集了20次
        //形成二维数组,模拟曲线
        //曲线近似度最近的数组表示可能为相同端口
        if (timeIfListMapList == null || timeIfListMapList.size() == 0) {
            return result;
        }
        //生成端口--曲线map
        Map<Integer, List<IfPoint>> portLineInMap = new LinkedHashMap<Integer, List<IfPoint>>();
        Map<Integer, List<IfPoint>> portLineOutMap = new LinkedHashMap<Integer, List<IfPoint>>();

        for (List<IfEntry> ifEntries : timeIfListMapList) {
            for (IfEntry ifEntry : ifEntries) {
                List<IfPoint> portsIn = portLineInMap.get(ifEntry.getIfIndex());
                List<IfPoint> portsOut = portLineOutMap.get(ifEntry.getIfIndex());
                if (portsIn == null) {
                    portsIn = new ArrayList<IfPoint>();
                }
                if (portsOut == null) {
                    portsOut = new ArrayList<IfPoint>();
                }
                if (ifEntry.getIfInOctets() > 0 && ifEntry.getIfOutOctets() > 0) {
                    portsIn.add(new IfPoint(ifEntry.getDataTime(), ifEntry.getIfInOctets()));
                    portsOut.add(new IfPoint(ifEntry.getDataTime(), ifEntry.getIfOutOctets()));
                    portLineInMap.put(ifEntry.getIfIndex(), portsIn);
                    portLineOutMap.put(ifEntry.getIfIndex(), portsOut);
                }
            }
        }
        for (Map.Entry<Integer, List<IfPoint>> inIfPointListEntry : portLineInMap.entrySet()) {
            List<IfPoint> lineList = createLine(inIfPointListEntry.getValue());
            portLineInMap.put(inIfPointListEntry.getKey(), lineList);
        }
        for (Map.Entry<Integer, List<IfPoint>> outIfPointListEntry : portLineOutMap.entrySet()) {
            List<IfPoint> lineList = createLine(outIfPointListEntry.getValue());
            result.put(outIfPointListEntry.getKey(), new List[]{portLineInMap.get(outIfPointListEntry.getKey()), lineList});
        }
        return result;
    }

    public List<IfPoint> createLine(List<IfPoint> IfPoints) {
        List<IfPoint> lineIfPointList = new ArrayList<IfPoint>();
        int length = IfPoints.size() - 1;
        for (int i = 0; i < length; i++) {
            IfPoint IfPoint = IfPoints.get(i);
            IfPoint IfPointNext = IfPoints.get(i + 1);
            Long x1 = IfPoint.x, x2 = IfPointNext.x;
            double y1 = IfPoint.y, y2 = IfPointNext.y;
            if (x1.equals(x2)) {
                // Tangent = NaN
                double from = Math.min(y1, y2);
                double to = Math.max(y1, y2);
                for (double y = from; y <= to; y++) {
                    lineIfPointList.add(new IfPoint(x1, y));
                }
            } else {
                double slope = ((y2 - y1)) / ((double) (x2 - x1));
                int step = (x2 > x1) ? 1000 : -1000;
                for (Long x = x1; x <= x2; x += step) {
                    double y = ((x - x1) * slope + y1);
                    lineIfPointList.add(new IfPoint(x, y));
                }
            }
        }
        return lineIfPointList;
    }


    /**
     * 多线程去取端口性能数据
     *
     * @param ips
     * @return
     */
    public Map<String, List<List<IfEntry>>> portPackagePool(List<String> ips) {
        int threadNum = 1000;
        Map<String, List<List<IfEntry>>> result = new HashMap<String, List<List<IfEntry>>>();
        ExecutorService service = Executors.newFixedThreadPool(threadNum);
        for (int i = 0; i < ips.size(); ) {
            String ip = ips.get(i);
            DeviceInfo deviceInfo = this.deviceManager.getDeviceInfoByIp(ip);
            service.execute(new PortPacakgeTask(ip, result, deviceInfo.getSnmpConfig()));
            i++;
            if (i % threadNum == 0) {
                if (!service.isShutdown()) {
                    service.shutdown();
                }
                try {
                    for (boolean bool = false; !bool; bool = service.awaitTermination(40, TimeUnit.SECONDS)) ;
                    service = Executors.newFixedThreadPool(threadNum);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        if (!service.isShutdown()) {
            service.shutdown();
        }
        try {
            for (boolean bool = false; !bool; bool = service.awaitTermination(40, TimeUnit.SECONDS)) ;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * @param deviceInfoList
     */
    private void DiscoveryMacLinks(List<DeviceInfo> deviceInfoList) {
        List<DeviceInfo> deviceInfos = this.deviceManager.switchList;
        List<DeviceInfo> deviceVector = new ArrayList<DeviceInfo>();
        Iterator<DeviceInfo> it = deviceInfos.iterator();
        while (it.hasNext()) {
            DeviceInfo upDevice = it.next();
            try {
                deviceVector.add(upDevice);
                List<PortInfo> portList = upDevice.getPortInfos();
                Iterator<PortInfo> it2 = portList.iterator();
                while (it2.hasNext()) {
                    PortInfo port = it2.next();
                    checkPortLeafDevice(port);
                    List<DeviceInfo> devices = port.subDeviceList;
                    Iterator<DeviceInfo> it3 = devices.iterator();
                    while (it3.hasNext()) {
                        DeviceInfo deviceInfo = it3.next();
                        if (deviceVector.contains(deviceInfo)) {
                            continue;
                        }
                        List<PortInfo> ports = deviceInfo.getPortInfos();
                        Iterator<PortInfo> it4 = ports.iterator();
                        while (it4.hasNext()) {
                            PortInfo portInfo2 = it4.next();
                            checkTwoPortLink(port, portInfo2);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                continue;
            }
        }
    }

    private void DiscoveryLLDPLinks(List<DeviceInfo> deviceInfoList) {
        Iterator<DeviceInfo> it1 = deviceInfoList.iterator();
        while (it1.hasNext()) {
            DeviceInfo upDeviceInfo = it1.next();
            try {
                Iterator<LldpRemEntry> it2 = upDeviceInfo.getLldpRemEntries()
                        .iterator();
                while (it2.hasNext()) {
                    LldpRemEntry lldpRemEntry = it2.next();
                    int port = lldpRemEntry.getLldpRemLocalPortNum();
                    String macOrip = lldpRemEntry.getLldpRemChassisId();// 邻居设备MAC or IP depends on subtype
                    DeviceInfo downDeviceInfo = this.deviceManager.getDeviceInfoByMAC(macOrip);
                    if (downDeviceInfo == null && REUtil.isIp(macOrip)) {
                        downDeviceInfo = this.deviceManager.getDeviceInfoByIp(macOrip);
                    }
                    if (downDeviceInfo == null) {
                        continue;//if there is not a mac then the data is meaningless;
                    }
                    PortInfo upPort = new PortInfo();
                    upPort.device = upDeviceInfo;
                    upPort.port = port;
                    upPort.isUseIfIndexPort = false;

                    PortInfo downPort = new PortInfo();
                    downPort.device = downDeviceInfo;
                    setRemoteLLDPPort(downPort, lldpRemEntry);

                    boolean flag = true;// inside link
                    if (upDeviceInfo != downDeviceInfo) {
                        flag = false;// outside link
                    }
                    LinkInfo newLink = LinkInfo.creatLink(upPort, downPort, flag);
                    addLink(newLink);
                }
            } catch (Exception e) {
                e.printStackTrace();
                continue;
            }
        }
    }

    private void DiscoveryCDPLinks(List<DeviceInfo> deviceInfoList) {
        Iterator<DeviceInfo> it = deviceInfoList.iterator();
        while (it.hasNext()) {
            DeviceInfo upDeviceInfo = it.next();
            try {
                Iterator<CdpCacheEntry> it2 = upDeviceInfo.getCdpCacheEntries()
                        .iterator();
                while (it2.hasNext()) {
                    CdpCacheEntry cdpCacheEntry = it2.next();
                    int ifIndex = cdpCacheEntry.getCdpCacheIfIndex();// IfIndex
                    String address = cdpCacheEntry.getCdpCacheAddress();// Address
                    DeviceInfo downDevice = this.deviceManager.getDeviceInfoByIp(address);
                    if (downDevice == null) {
                        downDevice = new DeviceInfo();
                        downDevice.setDeviceIP(address);
                    }
                    PortInfo upPort = new PortInfo();
                    upPort.device = upDeviceInfo;
                    upPort.port = ifIndex;
                    upPort.isUseIfIndexPort = true;
                    PortInfo downPort = new PortInfo();
                    downPort.device = downDevice;
                    setRemoteCDPPort(downPort, cdpCacheEntry);
                    boolean flag = true;//内部链接
                    if (upDeviceInfo != downDevice) {
                        flag = false;//外部链接
                    }
                    LinkInfo newLink = LinkInfo.creatLink(upPort, downPort, flag);
                    addLink(newLink);
                }
            } catch (Exception e) {
                e.printStackTrace();
                continue;
            }
        }
    }

    private void DiscoveryStpLinks(List<DeviceInfo> deviceInfoList) {
        Iterator<DeviceInfo> it1 = deviceInfoList.iterator();
        while (it1.hasNext()) {
            DeviceInfo upDevice = it1.next();
            try {
                Iterator<Dot1dStpPortEntry> it2 = upDevice.getDot1dStpPortEntries()
                        .iterator();
                while (it2.hasNext()) {
                    Dot1dStpPortEntry stpPortEntry = it2.next();
                    if (stpPortEntry.getDot1dStpPortState() != null && stpPortEntry.getDot1dStpPortState() == 2) {
                        Iterator<PortInfo> it3 = upDevice.getPortInfos().iterator();
                        while (it3.hasNext()) {
                            PortInfo portInfo = it3.next();
                            if (portInfo.port != stpPortEntry.getDot1dStpPort()) {
                                continue;
                            }
                            portInfo.isBlocking = true;
                        }
                    }
                    String addr = stpPortEntry.getDot1dStpPortDesignatedBridge() == null ? "" : stpPortEntry.getDot1dStpPortDesignatedBridge().substring(6);
                    DeviceInfo downDevice = this.deviceManager.getDeviceInfoByMAC(addr);
                    if (downDevice == null) {
                        downDevice = new DeviceInfo();
                        String ip = this.deviceManager.getIpofMac(addr);
                        if (ip == null) {
                            continue;
                        }
                        downDevice.setDeviceIP(ip);
                        downDevice.setDeviceMAC(addr.toUpperCase());
                    }
                    PortInfo upPort = new PortInfo();
                    upPort.device = upDevice;
                    upPort.port = stpPortEntry.getDot1dStpPort();
                    upPort.isUseIfIndexPort = false;
                    String portX = stpPortEntry.getDot1dStpPortDesignatedPort();
                    portX = portX.substring(portX.indexOf(":") + 1);
                    int j = new BigInteger(portX, 16).intValue();

                    PortInfo downPort = new PortInfo();
                    downPort.device = downDevice;
                    downPort.port = j;
                    downPort.isUseIfIndexPort = false;

                    boolean flag = true;//内部链接
                    if (upDevice != downDevice) {
                        flag = false;//外部链接
                    }
                    LinkInfo localLinkInfo = LinkInfo.creatLink(upPort, downPort, flag);
                    addLink(localLinkInfo);
                }
            } catch (Exception e) {
                e.printStackTrace();
                continue;
            }
        }
    }

    private void DiscoveryOspfNbrLinks(List<DeviceInfo> deviceInfoList) {
        Iterator<DeviceInfo> it1 = deviceInfoList.iterator();
        while (it1.hasNext()) {
            DeviceInfo upDevice = it1.next();
            try {
                Iterator<OspfNbrEntry> it2 = upDevice.getOspfNbrEntries().iterator();
                while (it2.hasNext()) {
                    OspfNbrEntry ospfNbrEntry = it2.next();
                    String ip = ospfNbrEntry.getOspfNbrIpAddr();
                    String id = ospfNbrEntry.getOspfNbrRtrId();// 32位整数在自治系统内唯一标识一台设备。
                    DeviceInfo downDevice = this.deviceManager.getDeviceInfoByIp(ip);
                    if (downDevice == null) {
                        downDevice = this.deviceManager.getDeviceInfoByIp(id);
                    }
                    if (downDevice == null) {
                        downDevice = new DeviceInfo();
                        downDevice.setDeviceIP(ip);
                    }
                    PortInfo upPort = new PortInfo();
                    upPort.device = upDevice;
                    upPort.port = Constants.UnknownPort;
                    upPort.isUseIfIndexPort = true;
                    int j = ospfNbrEntry.getOspfNbrAddressLessIndex();
                    if (j == 0) {
                        j = downDevice.getIfIndexOfIPAddress(ip);
                        if (-1 == j) {
                            j = downDevice.getIfIndexOfIPAddress(id);
                        }
                    }
                    PortInfo downPort = new PortInfo();
                    downPort.device = downDevice;
                    downPort.port = j;
                    downPort.isUseIfIndexPort = true;

                    boolean flag = true;//内部链接
                    if (upDevice != downDevice) {
                        flag = false;//外部链接
                    }
                    LinkInfo link = LinkInfo.creatLink(upPort, downPort, flag);
                    addLink(link);
                }
            } catch (Exception e) {
                e.printStackTrace();
                continue;
            }
        }
    }

    private void DiscoveryIPRouterLinks(List<DeviceInfo> deviceInfoList) {
        Iterator<DeviceInfo> it1 = deviceInfoList.iterator();
        while (it1.hasNext()) {
            DeviceInfo upDevice = it1.next();
            try {
                Iterator<IpRouteEntry> it2 = upDevice.getIpRouteEntries().iterator();
                while (it2.hasNext()) {
                    IpRouteEntry ipRouterEntry = it2.next();
                    String nextHop = ipRouterEntry.getIpRouteNextHop();// 下一跳
                    String routeDest = ipRouterEntry.getIpRouteDest();// 路由地址
                    if (ipRouterEntry.getIpRouteType() == 3
                            || nextHop.equalsIgnoreCase(upDevice.getDeviceIP())
                            || upDevice.getIpAddrEntries().contains(nextHop)) {
                        continue;
                    }
                    DeviceInfo downDevice = this.deviceManager
                            .getDeviceInfoByIp(nextHop);
                    if (downDevice == null) {
                        downDevice = new DeviceInfo();
                        downDevice.setDeviceIP(nextHop);
                    }

                    PortInfo upPort = new PortInfo();
                    upPort.device = upDevice;
                    upPort.port = ipRouterEntry.getIpRouteIfIndex();
                    upPort.isUseIfIndexPort = true;
                    int j = downDevice.getIfIndexOfIPAddress(nextHop);

                    PortInfo downPort = new PortInfo();
                    downPort.device = downDevice;
                    downPort.port = j;
                    downPort.isUseIfIndexPort = true;

                    boolean flag = true;//内部链接
                    if (upDevice != downDevice) {
                        flag = false;//外部链接
                    }
                    LinkInfo link = LinkInfo.creatLink(upPort, downPort, flag);
                    addLink(link);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private void DiscoveryArpLinks(List<DeviceInfo> deviceInfoList) {
        Iterator<DeviceInfo> it1 = deviceInfoList.iterator();
        while (it1.hasNext()) {
            DeviceInfo upDevice = it1.next();
            try {
                Iterator<IpNetToMediaEntry> it2 = upDevice.getIpNetToMediaEntries().iterator();
                while (it2.hasNext()) {
                    IpNetToMediaEntry ipNetToMediaEntry = it2.next();
                    String netip = ipNetToMediaEntry.getIpNetToMediaNetAddress();// ip地址
                    int port = ipNetToMediaEntry.getIpNetToMediaIfIndex();//端口地址
                    if (netip == null || netip.equalsIgnoreCase(upDevice.getDeviceIP())) {
                        continue;
                    }
                    DeviceInfo downDevice = this.deviceManager
                            .getDeviceInfoByIp(netip);
                    if (downDevice == null) {
                        continue;
//						downDevice = new DeviceInfo();
//						downDevice.setDeviceIP(netip);
                    }

                    PortInfo upPort = new PortInfo();
                    upPort.device = upDevice;
                    upPort.port = port;
                    upPort.isUseIfIndexPort = true;
                    int j = downDevice.getIfIndexOfIPAddress(netip);

                    PortInfo downPort = new PortInfo();
                    downPort.device = downDevice;
                    downPort.port = j;
                    downPort.isUseIfIndexPort = true;

                    boolean flag = true;//内部链接
                    if (upDevice != downDevice) {
                        flag = false;//外部链接
                    }
                    LinkInfo link = LinkInfo.creatLink(upPort, downPort, flag);
                    addLink(link);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private void setRemoteCDPPort(PortInfo downPort,
                                  CdpCacheEntry cdpCacheEntry) {
        DeviceInfo device = downPort.device;
        String port = cdpCacheEntry.getCdpCacheDevicePort();
        if (port.length() >= 1) {
            Iterator<IfEntry> it = device.getIfEntries().iterator();
            while (it.hasNext()) {
                IfEntry localMibIfEntry = it.next();
                if ((!localMibIfEntry.getIfDescr().equalsIgnoreCase(port))
                        && (!localMibIfEntry.getIfDescr()
                        .equalsIgnoreCase(port))
                        && (!localMibIfEntry.getIfPhysAddress()
                        .equalsIgnoreCase(port))) {
                    continue;
                }
                downPort.port = localMibIfEntry.getIfIndex();
                downPort.isUseIfIndexPort = true;
                return;
            }
        }
        try {
            if (REUtil.isNumber(port)) {
                downPort.port = Integer.parseInt(port);
                downPort.isUseIfIndexPort = true;
            } else {
                if (port.contains(Constants.Slash)) {    //		Gi1/0/3
                    String[] strs = port.split(Constants.Slash);
                    if (REUtil.isNumber(strs[strs.length - 1])) {
                        downPort.port = Integer.parseInt(strs[strs.length - 1]);
                        downPort.isUseIfIndexPort = true;
                    }
                }
            }
            return;
        } catch (Exception e) {
            downPort.port = device.getIfIndexOfIPAddress(cdpCacheEntry
                    .getCdpCacheAddress());
            downPort.isUseIfIndexPort = true;
        }
    }

    private void setRemoteLLDPPort(PortInfo downPort, LldpRemEntry lldpRemEntry) {
        String portId = lldpRemEntry.getLldpRemPortId();// 端口ID GigabitEthernet1/0/11
        String portDesc = lldpRemEntry.getLldpRemPortDesc();// 端口描述 GigabitEthernet1/0/11 Interface

        if (Constants.UnknownPort == downPort.port && portId.length() >= 1 && downPort.device.getIfEntries() != null) {
            Iterator<IfEntry> it = downPort.device.getIfEntries().iterator();
            while (it.hasNext()) {
                IfEntry ifEntry = it.next();
                if (!ifEntry.getIfDescr().equalsIgnoreCase(portId)
                        && !ifEntry.getIfPhysAddress().equalsIgnoreCase(portId)) {
                    continue;
                }
                downPort.port = ifEntry.getIfIndex();
                downPort.isUseIfIndexPort = true;
                break;
            }
        }

        if (Constants.UnknownPort == downPort.port && portDesc.length() >= 1 && downPort.device.getIfEntries() != null) {
            Iterator<IfEntry> it = downPort.device.getIfEntries().iterator();
            while (it.hasNext()) {
                IfEntry ifEntry = it.next();
                if (!ifEntry.getIfDescr().equalsIgnoreCase(portDesc)
                        && !ifEntry.getIfPhysAddress().equalsIgnoreCase(
                        portDesc)) {
                    continue;
                }
                downPort.port = ifEntry.getIfIndex();
                downPort.isUseIfIndexPort = true;
                break;
            }
        }

        if ((Constants.UnknownPort == downPort.port) && (portId.length() >= 1) && downPort.device.getLldpLocPortEntries() != null) {
            Iterator<LldpLocPortEntry> it = downPort.device.getLldpLocPortEntries().iterator();
            while (it.hasNext()) {
                LldpLocPortEntry lldpLocPortEntry = it.next();
                if (!lldpLocPortEntry.getLldpLocPortId().equalsIgnoreCase(
                        portId)) {
                    continue;
                }
                downPort.port = lldpLocPortEntry.getLldpLocPortNum();
                downPort.isUseIfIndexPort = false;
                break;
            }
        }

        try {
            if (REUtil.isNumber(portId)) {
                downPort.port = Integer.parseInt(portId);
                downPort.isUseIfIndexPort = false;
            } else {
                if (portId.contains(Constants.Slash)) {    //		Gi1/0/3
                    String[] strs = portId.split(Constants.Slash);
                    if (REUtil.isNumber(strs[strs.length - 1])) {
                        downPort.port = Integer.parseInt(strs[strs.length - 1]);
                        downPort.isUseIfIndexPort = false;
                    }
                }
            }
        } catch (Exception e) {
        }

        if (Constants.UnknownPort == downPort.port && downPort.device.getDot1dBasePortEntries() != null) {
            Iterator<Dot1dBasePortEntry> it = downPort.device.getDot1dBasePortEntries().iterator();
            while (it.hasNext()) {
                Dot1dBasePortEntry localDot1dBasePortEntry = it.next();
                int i = localDot1dBasePortEntry.getDot1dBasePort();
                if (!portId.contains(i + "")) {
                    continue;
                }
                downPort.port = i;
                downPort.isUseIfIndexPort = false;
                break;
            }
        }
    }

    private void addLink(LinkInfo linkInfo) {
        if (linkInfo == null) {
            return;
        }
        if (linkInfo.startDevice.equals(linkInfo.endDevice)) {
            return;
        }
        if (this.allLinkList.contains(linkInfo)) {
            return;
        }
        //添加判断，存在反向相同的链接，不再添加 如 A:1-->B:2 等价 B:2-->A:1
//		for(LinkInfo existLink:this.allLinkList){
//			String startIp = linkInfo.startDevice.getDeviceIP();
//			int startPort = linkInfo.startPort;
//			String endIp = linkInfo.endDevice.getDeviceIP();
//			int endPort = linkInfo.endPort;
//			String existstartIp = existLink.startDevice.getDeviceIP();
//			int existstartPort = existLink.startPort;
//			String existendIp = existLink.endDevice.getDeviceIP();
//			int existendPort = existLink.endPort;
//			//链路完全相同
//			if(startIp.equals(existstartIp)&&startPort==existstartPort&&endIp.equals(existendIp)&&endPort==existendPort) {
//				return;
//			}
//			//链路反向相同
//			if(startIp.equals(existendIp)&&startPort==existendPort&&endIp.equals(existstartIp)&&endPort==existstartPort) {
//				return;
//			}
//		}
//		System.out.println("发现链路"+linkInfo.toString());
        ListIterator<LinkInfo> it = this.allLinkList.listIterator();
        while (it.hasNext()) {
            LinkInfo link = it.next();
            int option = link.checkFuzzyOrAccurate(linkInfo);
            if (option == Constants.RemoveOld) {
                it.remove();
            } else {
                if (option != Constants.DropNew) {
                    continue;
                }
                return;
            }
        }
        this.allLinkList.add(linkInfo);
    }

    private void checkPortLeafDevice(PortInfo upPort) {
        if (upPort.subDeviceList.size() == 1) {
            DeviceInfo deviceInfo = upPort.subDeviceList.get(0);
            PortInfo downPort = new PortInfo();
            downPort.device = deviceInfo;
            downPort.port = Constants.UnknownPort;
            LinkInfo linkInfo = LinkInfo.creatLink(upPort, downPort, true);
            addLink(linkInfo);
            return;
        }
        if (upPort.portMacList.size() == 1) {
            String mac = upPort.portMacList.get(0);
            if (this.deviceManager.getDeviceInfoByMAC(mac) == null) {
                String ip = this.deviceManager.getIpofMac(mac);
                if (ip != null) {
                    DeviceInfo deviceInfo = new DeviceInfo();
                    deviceInfo.setDeviceIP(ip);
                    deviceInfo.setDeviceMAC(mac.toUpperCase());
                    PortInfo downPort = new PortInfo();
                    downPort.device = (deviceInfo);
                    downPort.port = Constants.UnknownPort;
                    LinkInfo link = LinkInfo.creatLink(upPort, downPort, true);
                    addLink(link);
                } else {
//没有找到IP的MAC地址先不发现 DeviceInfo deviceInfo = new DeviceInfo();
//					deviceInfo.setDeviceIP("");
//					deviceInfo.setDeviceMAC(mac.toUpperCase());
//					PortInfo downPort = new PortInfo();
//					downPort.device = deviceInfo;
//					downPort.port = Constants.UnknownPort;
//					LinkInfo link = LinkInfo.creatLink(upPort,downPort, false);
//					addLink(link);
                }
            }
        }
    }

    private void checkTwoPortLink(PortInfo port1, PortInfo port2) {
        DeviceInfo device1 = port1.device;
        List<DeviceInfo> connDeviceList1 = port1.subDeviceList;
        DeviceInfo device2 = port2.device;
        List<DeviceInfo> connDeviceList2 = port2.subDeviceList;

        if (connDeviceList1.contains(device2) && connDeviceList2.contains(device1)) {
            ArrayList<DeviceInfo> deviceInfoList = new ArrayList<DeviceInfo>();
            Iterator<DeviceInfo> it = connDeviceList1.iterator();
            while (it.hasNext()) {
                DeviceInfo device = it.next();
                if (!connDeviceList2.contains(device)) {
                    continue;
                }
                deviceInfoList.add(device);
            }
            if (deviceInfoList.size() <= 0) {
                LinkInfo link = LinkInfo.creatLink(port1, port2, true);
                addLink(link);

            } else if (deviceInfoList.size() == 1) {
                DeviceInfo device = deviceInfoList.get(0);
                PortInfo port = new PortInfo();
                port.device = device;
                port.port = Constants.UnknownPort;
                LinkInfo link1 = LinkInfo.creatLink(port1, port, true);
                LinkInfo link2 = LinkInfo.creatLink(port2, port, true);
                addLink(link1);
                addLink(link2);
            }
        }
    }
}